﻿using System;
using System.Diagnostics.CodeAnalysis;

namespace App.Console
{
    [SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
    public class Gauss
    {
        public float[,] Result { get; set; }

        public bool Solve(float[,] m)
        {
            var rowCount = m.GetUpperBound(0) + 1;
            if (m == null || m.Length != rowCount*(rowCount + 1))
                throw new ArgumentException("Este algoritmo precisa ser alimentado com uma matriz do tipo (n x n+1).");
            if (rowCount < 1)
                throw new ArgumentException("A matriz precisa ter pelo menos uma linha.");
            
            for (var col = 0; col + 1 < rowCount; col++)
                if (m[col, col] == 0)
                {
                    var swapRow = col + 1;
                    for (; swapRow < rowCount; swapRow++) if (m[swapRow, col] != 0) break;

                    if (m[swapRow, col] != 0)
                    {
                        var tmp = new float[rowCount + 1];
                        for (var i = 0; i < rowCount + 1; i++)
                        {
                            tmp[i] = m[swapRow, i];
                            m[swapRow, i] = m[col, i];
                            m[col, i] = tmp[i];
                        }
                    }
                    else return false;
                }

            for (var sourceRow = 0; sourceRow + 1 < rowCount; sourceRow++)
            {
                for (var destRow = sourceRow + 1; destRow < rowCount; destRow++)
                {
                    var df = m[sourceRow, sourceRow];
                    var sf = m[destRow, sourceRow];
                    for (var i = 0; i < rowCount + 1; i++)
                        m[destRow, i] = m[destRow, i]*df - m[sourceRow, i]*sf;
                }
            }

            for (var row = rowCount - 1; row >= 0; row--)
            {
                var f = m[row, row];
                if (f == 0) return false;

                for (var i = 0; i < rowCount + 1; i++) m[row, i] /= f;
                for (var destRow = 0; destRow < row; destRow++)
                {
                    m[destRow, rowCount] -= m[destRow, row]*m[row, rowCount];
                    m[destRow, row] = 0;
                }
            }

            Result = m;

            return true;
        }
    }
}